# -*- mode: python; coding: utf-8; -*-
##--------------------------------------------------------------------------##
##
## Copyright (C) 1998-2003 Markus Oberhumer <markus@oberhumer.com>
## Copyright (C) 2003 Mt. Hood Playing Card Co.
## Copyright (C) 2005-2010 Skomoroh <skomoroh@users.sourceforge.net>
## Copyright (C) 2010-2013 Jeremy Austin-Bardo <tjaustinbardo@gmail.com>
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program.  If not, see <http://www.gnu.org/licenses/>.
##
##--------------------------------------------------------------------------##
__docformat__ = "restructuredtext en"

from .. import comparecards as cmp_cards
from .. import formlib as layout
from .. import hint
from .. import pilelib as stack
from .. import quickplayscore as quickplay
from .. import stackfill as fill
from ..gamelib import info as gi, state, game
from ..packlib import info as cs, place
from ..utillib.misc import kwdefault
from ..forms import (canfield as CanfieldLayout,
                     streetsalleys as StreetsAlleysLayout,
                     klondike as KlondikeLayout,
                     freecell as FreeCellLayout,
                     fortythieves as FortyThievesLayout,)
from ..piles import (spider as SpiderStack, freecell as FreeCellStack,
                     foundation as FoundationStack,)

Wrap = stack.StackWrapper
GT, SL = gi.GT, gi.SL


class FirstFinish_UD_AC_RowStack(stack.FirstFinishMethod,
                                 stack.UD_AC_RowStack):
    """
    Foundation must have at least 1 card before build. Build either in rank
    by alt color. Play top.
    """
    pass


class FirstFinish_Reserve(stack.FirstFinishMethod, stack.OpenStack):
    """
    Foundation must have at least 1 card before build. Build none. Play top.
    """
    pass


class StreetsAndAlleys(game.Game):
    """
    Finish 1 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.streetsalleys.StreetsAlleysLayout
        - row [4 vert E, 4 vert W, horz offset]
        - talon [1 SE, no offset]
        - foundation [4 vert, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.InitialDealTalon
        No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.SS_Foundation (max_move=0)
        Build up in rank by same suit. Play none. Base ace.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.SuperMoveRK_RowStack
        Init all open, min 6 card. Build down in rank alone. Play top or
        sequence if possible as seperate plays. Base none.

       _reserve_ : None.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=0xc3e2, name='Streets and Alleys',
                    game_type=(GT.BELEAGUERED_CASTLE | GT.OPEN | GT.GNOME),
                    decks=1, skill_level=SL.MOSTLY_SKILL, version='3.30')
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _layout_ = StreetsAlleysLayout.StreetsAlleysLayout
    _talon_ = stack.InitialDealTalon
    _foundation_ = Wrap(stack.SS_Foundation, max_move=0)
    _row_ = stack.SuperMoveRK_RowStack
    _reserve_ = None
    _hint_ = hint.CautiousDefaultHint

    def _create_game(self, **kw):
        """
        _create_game([rows=8, reserves=0, playcards=13]) -> Layout

        :returns: current layout with piles and their texts set.

        :seealso: `pysollib.layouts.streetsalleys` for keyword definitions.
        """
        kwdefault(kw, rows=8, playcards=13, reserves=0)
        return super(StreetsAndAlleys, self)._create_game(**kw)

    def _start(self, rows=7, **kw):
        """
        Deal initial cards for new game.
          - s[rows] ~7 ea up.

        .. \_start([rows=7]) -> None
        """
        for i in range(rows - 2):
            self.s['talon'].dealRow(frames=0)
        self.s['talon'].dealRow()
        self.s['talon'].dealRowAvail()


class BeleagueredCastle(StreetsAndAlleys):
    """
    Finish 1 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.streetsalleys.StreetsAlleysLayout
        - row [4 vert E, 4 vert W, horz offset]
        - talon [1 SE, no offset]
        - foundation [4 vert, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.InitialDealTalon
        No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.SS_Foundation (max_move=0)
        Init ace per. Build up in rank by same suit. Play none. Base ace.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.SuperMoveRK_RowStack
        Init 6 open card per. Build down in rank alone.
        Play top or sequence if possible as seperate plays. Base none.

       _reserve_ : None.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=0xc372, name='Beleaguered Castle',
                    game_type=(GT.BELEAGUERED_CASTLE | GT.OPEN | GT.GNOME),
                    decks=1, skill_level=SL.MOSTLY_SKILL, version='2.90')
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return ACE on top by suit, deck for _foundation_ deal.
        """
        return place.MoveToTop(cards, place.AllAce)

    def _start(self, rows=6, **kw):
        """
        Deal initial cards for new game.
          - s[foundations] 1 ea.
          - s[rows] ~6 ea up.

        .. \_start([rows=6]) -> None
        """
        self.s['talon'].dealRow(rows=self.s['foundations'], frames=0)
        super(BeleagueredCastle, self)._start(rows=rows, **kw)


class Citadel(StreetsAndAlleys):
    """
    Finish 1 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.streetsalleys.StreetsAlleysLayout
        - row [4 vert E, 4 vert W, horz offset]
        - talon [1 SE, no offset]
        - foundation [4 vert, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.InitialDealTalon
        No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.SS_Foundation (max_move=0)
        Build up in rank by same suit. Play none. Base ace.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.SuperMoveRK_RowStack
        Init all open, min 6 card, with finish without replace. Build down in
        rank alone. Play top or sequence if possible as seperate plays. Base
        none.

       _reserve_ : None.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=0xc3e1, name='Citadel',
                    game_type=(GT.BELEAGUERED_CASTLE | GT.OPEN), decks=1,
                    skill_level=SL.MOSTLY_SKILL, version='3.30')
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    def _start(self, **kw):
        """
        Deal initial cards for new game.
          - s[foundations] 1 ea.
          - s[rows] row or drop.

        .. \_start([]) -> None
        """
        self.s['talon'].dealRow(rows=self.s['foundations'], frames=0)
        while self.s['talon'].cards:
            self.s['talon'].deal_row_or_drop(ncards=cs.MAX)

class ExiledKings(Citadel):
    """
    Finish 1 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.streetsalleys.StreetsAlleysLayout
        - row [4 vert E, 4 vert W, horz offset]
        - talon [1 SE, no offset]
        - foundation [4 vert, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.InitialDealTalon
        No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.SS_Foundation (max_move=0)
        Build up in rank by same suit. Play none. Base ace.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.RK_RowStack (base_rank=cs.KING)
        Init all open, min 6 card with finish without replace. Build down in
        rank alone. Play sequence. Base king.

       _reserve_ : None.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=0xc567, name='Exiled Kings',
                    game_type=(GT.BELEAGUERED_CASTLE | GT.OPEN), decks=1,
                    skill_level=SL.MOSTLY_SKILL, version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _row_ = Wrap(stack.RK_RowStack, base_rank=cs.KING)


class Stronghold(StreetsAndAlleys):
    """
    Finish 1 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.streetsalleys.StreetsAlleysLayout
        - row [4 vert E, 4 vert W, horz offset]
        - talon [1 SE, no offset]
        - foundation [4 vert, no offset]
        - reserve [1 N, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.InitialDealTalon
        No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.SS_Foundation (max_move=0)
        Build up in rank by same suit. Play none. Base ace.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.SuperMoveRK_RowStack
        Init all open, min 6 card. Build down in rank alone. Play top or
        sequence if possible as seperate plays. Base none.

       _reserve_ : pysollib.stack.Stack
         .. pysollib.stack.ReserveStack
         Build any top. Play top. Max 1 card.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=0xc47c, name='Stronghold',
                    game_type=(GT.BELEAGUERED_CASTLE | GT.OPEN), decks=1,
                    skill_level=SL.MOSTLY_SKILL, version='0.80fc')
    __author__ = 'Skomoroh'
    __credits__ = ['Thomas Warfield']
    __version__ = 7.70

    _reserve_ = stack.ReserveStack

    def _create_game(self, **kw):
        """
        _create_game([reserves=1]) -> Layout

        :returns: current layout with piles and their texts set.

        :seealso: `pysollib.layouts.streetsalleys` for keyword definitions.
        """
        kw.setdefault('reserves', 1)
        return super(Stronghold, self)._create_game(**kw)


class Fastness(Stronghold):
    """
    Finish 1 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.streetsalleys.StreetsAlleysLayout
        - row [4 vert E, 4 vert W, horz offset]
        - talon [1 SE, no offset]
        - foundation [4 vert, no offset]
        - reserve [2 horz N, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.InitialDealTalon
        No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.SS_Foundation (max_move=0)
        Build up in rank by same suit. Play none. Base ace.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.SuperMoveRK_RowStack
        Init all open, min 6 card. Build down in rank alone. Play top or
        sequence if possible as seperate plays. Base none.

       _reserve_ : pysollib.stack.Stack
         .. pysollib.stack.ReserveStack
         Build any top. Play top. Max 1 card.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=0xc47d, name='Fastness',
                    game_type=(GT.BELEAGUERED_CASTLE | GT.OPEN | GT.ORIGINAL),
                    decks=1, skill_level=SL.MOSTLY_SKILL, version='0.80fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    def _create_game(self, **kw):
        """
        _create_game([reserves=2]) -> Layout

        :returns: current layout with piles and their texts set.

        :seealso: `pysollib.layouts.streetsalleys` for keyword definitions.
        """
        kw.setdefault('reserves', 2)
        return super(Fastness, self)._create_game(**kw)


class CastleOfIndolence(StreetsAndAlleys):
    """
    Finish 2 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.streetsalleys.StreetsAlleysLayout
        - row [4 vert E, 4 vert W, horz offset]
        - talon [1 SE, no offset]
        - foundation [8 over 2 col, no offset]
        - reserve [4 horz N, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.InitialDealTalon
        No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.RK_Foundation (max_move=0)
        Build up in rank by same suit. Play none. Base ace.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.RK_RowStack (max_move=1, max_accept=1)
        Init all open, min 6 card. Build down in rank alone. Play top.

       _reserve_ : pysollib.stack.Stack
         .. pysollib.stack.OpenStack
         Init 13 card per. Build none. Play top.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=0xc4d9, name='Castle of Indolence',
                    game_type=GT.BELEAGUERED_CASTLE, decks=2,
                    skill_level=SL.MOSTLY_SKILL, version='0.90fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _foundation_ = Wrap(stack.RK_Foundation, max_move=0)
    _row_ = Wrap(stack.RK_RowStack, max_move=1, max_accept=1)
    _reserve_ = stack.OpenStack

    def _create_game(self, **kw):
        """
        _create_game([reserves=4, reserve_texts=True]) -> Layout

        :returns: current layout with piles and their texts set.

        :seealso: `pysollib.layouts.streetsalleys` for keyword definitions.
        """
        kwdefault(kw, reserves=4, reserve_texts=True)
        return super(CastleOfIndolence, self)._create_game(**kw)

    def _start(self, reserves=13, **kw):
        """
        Deal initial cards for new game.
          - s[reserves] 13 ea up.
          - s[rows] ~7 ea up.

        .. \_start([rows=7]) -> None
        """
        for i in range(reserves):
            self.s['talon'].dealRow(rows=self.s['reserves'], frames=0)
        super(CastleOfIndolence, self)._start(**kw)


class SelectiveCastle(StreetsAndAlleys):
    """
    Finish 1 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.streetsalleys.StreetsAlleysLayout
        - row [4 vert E, 4 vert W, horz offset]
        - talon [1 SE, no offset]
        - foundation [4 vert, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.InitialDealTalon
        No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.Vary_SS_Foundation (max_move=0)
        Build up in rank by same suit. Play none. Base 1st card.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.RK_RowStack  (mod=13)
        Init all open, min 6 card. Build down in rank alone wrap. Play
        sequence.

       _reserve_ : None.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=0xc55c, name='Selective Castle',
                    game_type=(GT.BELEAGUERED_CASTLE | GT.OPEN), decks=1,
                    skill_level=SL.MOSTLY_SKILL, version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _foundation_ = stack.Vary_SS_Foundation
    _row_ = Wrap(stack.RK_RowStack, mod=13)

    def _create_game(self, **kw):
        """
        _create_game([]) -> Layout

        Add a canvas info label for base_rank next to last foundation.

        :returns: current layout with piles and their texts set.

        :seealso: `pysollib.layouts.streetsalleys` for keyword definitions.
        """
        l, s = super(SelectiveCastle, self)._create_game(**kw), self.s
        self.texts['info'] = l.create_info_text(s['foundations'][-1], anchor='ss')
        return l

    @state.not_state('is_preview')
    def _update_text(self):
        """
        _update_text() -> None

        Update canvas info label with base_rank of foundation.
        """
        if self.texts['info']:
            return
        base_rank = self.s['foundations'][0].cap['base_rank']
        text = '' if base_rank == cs.ANY else self.RANKS[base_rank]
        self.texts['info'].config(text=text)


class PenelopesWeb(StreetsAndAlleys):
    """
    Finish 1 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.streetsalleys.StreetsAlleysLayout
        - row [4 vert E, 4 vert W, horz offset]
        - talon [1 SE, no offset]
        - foundation [4 vert, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.InitialDealTalon
        No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.Vary_SS_Foundation (max_move=0)
        Build up in rank by same suit. Play none. Base ace.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.RK_RowStack  (base_rank=KING)
        Init all open, min 6 card. Build down in rank alone. Play sequence.
        Base king.

       _reserve_ : None.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=0xc5e9, name='Penelopeʼs Web',
                    game_type=(GT.BELEAGUERED_CASTLE | GT.OPEN), decks=1,
                    skill_level=SL.MOSTLY_SKILL,
                    version='0.94fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _row_ = Wrap(stack.RK_RowStack, base_rank=cs.KING)


class TenByOne(game.Game):
    """
    Finish 1 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.freecell.FreeCellLayout
        - row [10 horz S reserve, horz offset]
        - reserve [1 horz NW, no offset]
        - talon [1 SE, no offset]
        - foundation [4 horz NE, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.InitialDealTalon
        No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.SS_Foundation
        Build up in rank by same suit. Play top. Base ace.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.UD_SS_RowStack
        Init all open, min 5 card. Build either in rank by same suit. Play
        top.

       _reserve_ : pysollib.stack.Stack
         .. pysollib.stack.ReserveStack
         Build any top. Play top. Max 1 card.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=0xc495, name='Ten by One',
                    game_type=(GT.BELEAGUERED_CASTLE | GT.OPEN), decks=1,
                    skill_level=SL.MOSTLY_SKILL, version='0.90fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _layout_ = FreeCellLayout.FreeCellLayout
    _talon_ = stack.InitialDealTalon
    _foundation_ = stack.SS_Foundation
    _row_ = stack.UD_SS_RowStack
    _reserve_ = stack.ReserveStack
    _hint_ = hint.CautiousDefaultHint

    def _create_game(self, **kw):
        """
        ([rows=10, reserves=1, texts=False, playcards=16])

        :returns: current layout with piles and their texts set.

        :seealso: `pysollib.layouts.freecell` for keyword definitions.
        """
        kwdefault(kw, rows=10, reserves=1, texts=False, playcards=16)
        return super(TenByOne, self)._create_game(**kw)

    def _start(self, rows=6, **kw):
        """
        Deal initial cards for new game.
          - s[rows] ~6 ea up.

        .. \_start([rows=6]) -> None
        """
        for i in range(rows - 2):
            self.s['talon'].dealRow(frames=0)
        self.s['talon'].dealRow()
        self.s['talon'].dealRowAvail()


class Bastion(TenByOne):
    """
    Finish 1 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.freecell.FreeCellLayout
        - row [10 horz S reserve, horz offset]
        - reserve [2 horz NW, no offset]
        - talon [1 SE, no offset]
        - foundation [4 horz NE, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.InitialDealTalon
        No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.SS_Foundation
        Build up in rank by same suit. Play top. Base ace.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.UD_SS_RowStack
        Init 5 open card per. Build either in rank by same suit. Play top.

       _reserve_ : pysollib.stack.Stack
         .. pysollib.stack.ReserveStack
         Init 1 card per. Build any top. Play top. Max 1 card.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=0xc494, name='Bastion',
                    game_type=(GT.BELEAGUERED_CASTLE | GT.OPEN), decks=1,
                    skill_level=SL.MOSTLY_SKILL, version='0.90fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    def _create_game(self, **kw):
        """
        _create_game([reserves=2]) -> Layout

        :returns: current layout with piles and their texts set.

        :seealso: `pysollib.layouts.freecell` for keyword definitions.
        """
        kw.setdefault('reserves', 2)
        return super(Bastion, self)._create_game(**kw)

    def _start(self, rows=5, **kw):
        """
        Deal initial cards for new game.
          - s[rows] ~5 ea up.
          - s[reserves] 1 ea.

        .. \_start([rows=6]) -> None
        """
        super(Bastion, self)._start(rows=rows, **kw)
        self.s['talon'].dealRow(rows=self.s['reserves'])


class CastlesEnd(Bastion):
    """
    Finish 1 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.freecell.FreeCellLayout
        - row [10 horz S reserve, horz offset]
        - reserve [2 horz NW, no offset]
        - talon [1 SE, no offset]
        - foundation [4 horz NE, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.InitialDealTalon
        No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.Vary_SS_Foundation
        Build up in rank by same suit. Play top. Base 1st card.

      _row_ : pysollib.stack.Stack
        .. pysollib.stacks.FirstFinish_UD_AC_RowStack (mod=13)
        Init 5 open card per. Build either in rank by alt color wrap. Play
        top after. 1 card must finish on foundation before further play.

       _reserve_ : pysollib.stack.Stack
         .. pysollib.stacks.FirstFinish_Reserve
         Init 1 card per. Build any top. Play top. Max 1 card. 1 card must
         finish on foundation before further play.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=0xc5da, name='Castles End',
                    game_type=(GT.BELEAGUERED_CASTLE | GT.OPEN), decks=1,
                    skill_level=SL.MOSTLY_SKILL, version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _foundation_ = stack.Vary_SS_Foundation
    _row_ = Wrap(FirstFinish_UD_AC_RowStack, mod=13)
    _reserve_ = FirstFinish_Reserve

    def _create_game(self):
        """
        _create_game([]) -> Layout

        Add a canvas info label for base_rank next to last foundation.

        :returns: current layout with piles and their texts set.

        :seealso: `pysollib.layouts.freecell` for keyword definitions.
        """
        l, s = Bastion._create_game(self), self.s
        self.texts['info'] = l.create_info_text(s['foundations'][-1], anchor='se')
        return l

    @state.not_state('is_preview')
    def _update_text(self):
        """
        _update_text() -> None

        Update canvas info label with base_rank of foundation.
        """
        if self.texts['info']:
            return
        base_rank = self.s['foundations'][0].cap['base_rank']
        text = '' if base_rank == cs.ANY else self.RANKS[base_rank]
        self.texts['info'].config(text=text)


class Fortress(game.Game):
    """
    Finish 1 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.klondike.KlondikeLayout
        - row [10 horz S foundation, vert offset]
        - talon [1 NW, no offset]
        - foundation [4 horz N, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.InitialDealTalon
        No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.SS_Foundation (max_move=0)
        Build up in rank by same suit. Play none. Base ace.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.UD_SS_RowStack
        Init all open, min 5 card. Build either in rank by same suit. Play
        top.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=0xc3e3, name='Fortress',
                    game_type=(GT.BELEAGUERED_CASTLE | GT.OPEN | GT.GNOME),
                    decks=1, skill_level=SL.SKILL, version='3.30')
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _layout_ = KlondikeLayout.KlondikeLayout
    _talon_ = stack.InitialDealTalon
    _foundation_ = stack.SS_Foundation
    _row_ = stack.UD_SS_RowStack
    _hint_ = hint.CautiousDefaultHint

    def _create_game(self, **kw):
        """
        _create_game([rows=10, texts=False, playcards=16]) -> Layout

        :returns: current layout with piles and their texts set.

        :seealso: `pysollib.layouts.klondike` for keyword definitions.
        """
        kwdefault(kw, rows=10, texts=False, playcards=16)
        return super(Fortress, self)._create_game(**kw)

    def _start(self, rows=6, **kw):
        """
        Deal initial cards for new game.
          - s[rows] ~6 ea up.

        .. \_start([rows=6]) -> None
        """
        for i in range(rows - 2):
            self.s['talon'].dealRow(frames=0)
        self.s['talon'].dealRow()
        self.s['talon'].dealRowAvail()


class Chessboard(Fortress):
    """
    Finish 1 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.klondike.KlondikeLayout
        - row [10 horz S foundation, vert offset]
        - talon [1 NW, no offset]
        - foundation [4 horz N, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.InitialDealTalon
        No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.Vary_SS_Foundation (max_move=0)
        Build up in rank by same suit. Play none. Base  1st card.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.UD_SS_RowStack (mod=13)
        Init all open, min 5 card. Build either in rank by same suit wrap.
        Play top.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=0xc3e4, name='Chessboard',
                    game_type=(GT.BELEAGUERED_CASTLE | GT.OPEN | GT.GNOME),
                    decks=1, skill_level=SL.SKILL, version='3.30')
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _foundation_ = Wrap(stack.Vary_SS_Foundation, max_move=0)
    _row_ = Wrap(stack.UD_SS_RowStack, mod=13)

    def _create_game(self, **kw):
        """
        _create_game([]) -> Layout

        Add a canvas info label for base_rank next to last foundation.

        :returns: current layout with piles and their texts set.

        :seealso: `pysollib.layouts.klondike` for keyword definitions.
        """
        l, s = super(Chessboard, self)._create_game(**kw), self.s
        self.texts['info'] = l.create_info_text(s['foundations'][-1], anchor='e')
        return l

    @state.not_state('is_preview')
    def _update_text(self):
        """
        _update_text() -> None

        Update canvas info label with base_rank of foundation.
        """
        if self.texts['info']:
            return
        base_rank = self.s['foundations'][0].cap['base_rank']
        text = '' if base_rank == cs.ANY else self.RANKS[base_rank]
        self.texts['info'].config(text=text)


class Chequers(Fortress):
    """
    Finish 1 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.klondike.KlondikeLayout
        - row [25 over 5 col S foundation, vert offset]
        - talon [1 NW, no offset]
        - foundation [8 over 2 row N, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.Talon
        No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stacks.foundation.AceKingSS_Foundation (max_move=0)
        Build in rank by same suit. N up; S down. Play none. Base ace N, king
        S.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.UD_SS_RowStack
        Init 4 open card per. Build either in rank by same suit. Play top. Gap
        deal 1 card from talon.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=0xc4af, name='Chequers',
                    game_type=GT.BELEAGUERED_CASTLE, decks=2,
                    skill_level=SL.MOSTLY_SKILL, version='0.90fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = stack.Talon
    _foundation_ = Wrap(FoundationStack.AceKingSS_Foundation, max_move=0)

    def _create_game(self, **kw):
        """
        _create_game([rows=25, ncols=5, playcards=7, texts=True]) -> Layout

        :returns: current layout with piles and their texts set.

        :seealso: `pysollib.layouts.klondike` for keyword definitions.
        """
        kwdefault(kw, rows=25, ncols=5, playcards=7, texts=True)
        return super(Chequers, self)._create_game(**kw)

    def _start(self, rows=4, **kw):
        """
        Deal initial cards for new game.
          - s[rows] ~4 ea up.

        .. \_start([rows=4]) -> None
        """
        return super(Chequers, self)._start(rows=rows, **kw)

    @state.move_state('fill')
    def _auto_fill(self, stack):
        """
        Any row gap fill 1 card from talon.

        *Parameters*
          stack : stack.Stack
            pile that just had cards play.

        *Requires*:
          `pysollib.stackfill.fromTalon` for further details.
        """
        fill.fromTalon(self, stack, self.s['rows'])


class Zerline(game.Game):
    """
    Finish 2 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.streetsalleys.StreetsAlleysLayout
        - row [4 vert E, 4 vert W, horz offset]
        - talon [1 SE, no offset]
        - foundation [8 over 2, no offset]
        - reserve [1 N, horz offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.WasteTalon
        Deal 1 card on waste on click. No redeal.

      _waste_ : pysollib.stack.Stack
        .. pysollib.stack.WasteStack
        Init 1 card per. Build none. Play top.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.SS_Foundation (base_rank=KING, dir=1, max_move=0,
          mod=13)
        Build up in rank by same suit wrap. Play none. Base king.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.RK_RowStack (max_move=1, max_accept=1,
          base_rank=QUEEN)
        Init 5 open card per. Build down in rank alone. Play top. Base queen.

       _reserve_ : pysollib.stack.Stack
         .. pysollib.stacks.freecell.Zerline_Reserve  (max_cards=4)
         Build any top from row. Play top. Max 4 card.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=0xc482, name='Zerline',
                    game_type=GT.BELEAGUERED_CASTLE, decks=2,
                    skill_level=SL.MOSTLY_SKILL, version='0.80fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _layout_ = StreetsAlleysLayout.StreetsAlleysLayout
    _talon_ = stack.WasteTalon
    _waste_ = stack.WasteStack
    _foundation_ = Wrap(stack.SS_Foundation, base_rank=cs.KING, dir=1,
                        max_move=0, mod=13)
    _row_ = Wrap(stack.RK_RowStack, max_move=1, max_accept=1,
                 base_rank=cs.QUEEN)
    _reserve_ = Wrap(FreeCellStack.Zerline_Reserve, max_cards=4)
    _hint_ = hint.CautiousDefaultHint

    def _create_game(self, **kw):
        """
        _create_game([rows=8, playcards=13, reserve=1, holdcards=4]) -> Layout

        :returns: current layout with piles and their texts set.

        :seealso: `pysollib.layouts.streetsalleys` for keyword definitions.
        """
        kwdefault(kw, rows=8, playcards=13, reserve=1, holdcards=4,
                  texts=True)
        return super(Zerline, self)._create_game(**kw)

    def _start(self, rows=5, **kw):
        """
        Deal initial cards for new game.
          - s[rows] 5 ea up.

        .. \_start([rows=5]) -> None
        """
        for i in range(rows - 1):
            self.s['talon'].dealRow(frames=0)
        self.s['talon'].dealRow()

    _quickplay_ = quickplay.RowStack


class Zerline3Decks(Zerline):
    """
    Finish 3 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.streetsalleys.StreetsAlleysLayout
        - row [4 vert E, 4 vert W, horz offset]
        - talon [1 SE, no offset]
        - talon [1 W talon, no offset]
        - foundation [12 over 3, no offset]
        - reserve [1 N, horz offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.WasteTalon
        Deal 1 card on waste on click. No redeal.

      _waste_ : pysollib.stack.Stack
        .. pysollib.stack.WasteStack
        Init 1 card per. Build none. Play top.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.SS_Foundation (base_rank=KING, dir=1, max_move=0,
          mod=13)
        Build up in rank by same suit wrap. Play none. Base king.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.RK_RowStack (max_move=1, max_accept=1,
          base_rank=QUEEN)
        Init 5 open card per. Build down in rank alone. Play top. Base queen.

       _reserve_ : pysollib.stack.Stack
         .. pysollib.stacks.freecell.Zerline_Reserve (max_cards=6)
         Build any top from row. Play top. Max 6 card.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=0xc4db, name='Zerline (3 decks)',
                    game_type=(GT.BELEAGUERED_CASTLE | GT.ORIGINAL), decks=3,
                    skill_level=SL.MOSTLY_SKILL, version='0.90fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _reserve_ = Wrap(FreeCellStack.Zerline_Reserve, max_cards=6)

    def _create_game(self, **kw):
        """
        _create_game([holdcards=6]) -> Layout

        :returns: current layout with piles and their texts set.

        :seealso: `pysollib.layouts.streetsalleys` for keyword definitions.
        """
        kw.setdefault('holdcards', 6)
        return super(Zerline, self)._create_game(**kw)


class Rittenhouse(game.Game):
    """
    Finish 2 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.fortythieves.FortyThievesLayout
        - row [9 horz S foundation, vert offset]
        - talon [1 SW, no offset]
        - foundation [8 horz N, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.InitialDealTalon
        No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stacks.foundation.Rittenhouse_Foundation (max_move=0)
        Build in rank alone from neighbor row, center any. W up; E down. Play
        none. Base ace W, king E.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.UD_RK_RowStack
        Init all open, min 11 card with finish without replace. Build down in
        rank alone. Play top. Auto-finish cards.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=0xc4e0, name='Rittenhouse',
                    game_type=(GT.BELEAGUERED_CASTLE | GT.OPEN), decks=2,
                    skill_level=SL.MOSTLY_SKILL, version='0.90fc')
    __author__ = 'Skomoroh'
    __credits__ = ['Captain Jeffrey T. Spaulding']
    __version__ = 7.70

    _layout_ = FortyThievesLayout.FortyThievesLayout
    _talon_ = stack.InitialDealTalon
    _foundation_ = Wrap(FoundationStack.Rittenhouse_Foundation, max_move=0)
    _row_ = stack.UD_RK_RowStack
    _hint_ = hint.CautiousDefaultHint

    def _create_game(self, **kw):
        """
        _create_game([rows=9, playcards=12, align=layout.LEFT]) -> Layout

        :returns: current layout with piles and their texts set.

        :seealso: `pysollib.layouts.fortythieves` for keyword definitions.
        """
        kwdefault(kw, rows=9, playcards=12, align=layout.LEFT)
        return super(Rittenhouse, self)._create_game(**kw)

    def _start(self, **kw):
        """
        Deal initial cards for new game.
          - s[rows] ~6 ea or drop.

        .. \_start([]) -> None
        """
        while self.s['talon'].cards:
            self.s['talon'].deal_row_or_drop(ncards=cs.MAX)

    @state.move_state('fill')
    def _auto_fill(self, stack):
        """
        Auto finish any top card that builds on foundation.

        *Parameters*:
          stack : stack.Stack
            pile that just had cards play.
        """
        pile = cmp_cards.groupTakes(self.s['foundations'], stack)
        if pile is not None:
            stack.move_pile(1, pile, frames=4)


class Lightweight(game.Game):
    """
    Finish 2 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.fortythieves.FortyThievesLayout
        - row [12 horz S foundation, vert offset]
        - talon [1 SE, no offset]
        - foundation [8 horz N, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.InitialDealTalon
        No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.SS_Foundation, (max_move=0)
        Init ace per. Build up in rank by same suit. Play none. Base ace.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.RK_RowStack (base_rank=KING)
        Init 8 open card per. Build down in rank alone. Play seq.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=0xc54b, name='Lightweight',
                    game_type=(GT.BELEAGUERED_CASTLE | GT.OPEN | GT.ORIGINAL),
                    decks=2, skill_level=SL.MOSTLY_SKILL,
                    version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _layout_ = FortyThievesLayout.FortyThievesLayout
    _talon_ = stack.InitialDealTalon
    _foundation_ = Wrap(stack.SS_Foundation, max_move=0)
    _row_ = Wrap(stack.RK_RowStack, base_rank=cs.KING)

    def _create_game(self, **kw):
        """
        _create_game([rows=12, playcards=20]) -> Layout

        :returns: current layout with piles and their texts set.

        :seealso: `pysollib.layouts.fortythieves` for keyword definitions.
        """
        kwdefault(kw, rows=12, playcards=20)
        return super(Lightweight, self)._create_game(**kw)

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return ACE on top by suit, deck for _foundation_ deal.
        """
        return place.MoveToTop(cards, place.AllAce)

    def _start(self, rows=8, **kw):
        """
        Deal initial cards for new game.
          - s[foundations] 1 ea.
          - s[rows] ~8 ea up.

        .. \_start([rows=8]) -> None
        """
        self.s['talon'].dealRow(rows=self.s['foundations'], frames=0)
        for i in range(rows - 1):
            self.s['talon'].dealRow(frames=0)
        self.s['talon'].dealRowAvail()


class CastleMount(Lightweight):
    """
    Finish 2 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.fortythieves.FortyThievesLayout
        - row [12 horz S foundation, vert offset]
        - talon [1 SE, no offset]
        - foundation [8 horz N, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.InitialDealTalon
        No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.SS_Foundation, (max_move=0)
        Init ace per. Build up in rank by same suit. Play none. Base ace.

      _row_ : pysollib.stack.Stack
        .. pysollib.stacks.spider.SS_RK_RowStack
        Init 8 open card per. Build down in rank alone. Play seq by same suit.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=0xc54c, name='Castle Mount',
                    game_type=(GT.BELEAGUERED_CASTLE | GT.OPEN), decks=3,
                    skill_level=SL.MOSTLY_SKILL,
                    version='0.93fc')
    __author__ = 'Skomoroh'
    __credits__ = ['Thomas Warfield']
    __version__ = 7.70

    _row_ = SpiderStack.SS_RK_RowStack

    _quickplay_ = quickplay.Spider


class Soother(game.Game):
    """
    Finish 4 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.canfield.CanfieldLayout
        - row [9 horz S foundation, vert offset]
        - talon [1 SE, no offset]
        - waste [1 E talon, no offset]
        - foundation [16 over 2 row N, no offset]
        - reserve [1 S talon, vert offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.WasteTalon
        Deal 1 card on waste on click. No redeal.

      _waste_ : pysollib.stack.Stack
        .. pysollib.stack.WasteStack
        Init 1 card per. Build none. Play top.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.SS_Foundation
        Init ace per. Build up in rank by same suit. Play top. Base ace.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.RK_RowStack (max_move=1, base_rank=KING)
        Init 5 open card per. Build down in rank alone. Play top. Base King

      _reserve_ : pysollib.stack.Stack
        .. pysollib.stacks.ReserveStack (max_cards=8)
        Build any top. Play top. Max 8 card.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=0xc5c2, name='Soother',
                    game_type=(GT.BELEAGUERED_CASTLE | GT.ORIGINAL), decks=4,
                    skill_level=SL.MOSTLY_SKILL, version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _layout_ = CanfieldLayout.CanfieldLayout
    _talon_ = stack.WasteTalon
    _waste_ = stack.WasteStack
    _foundation_ = stack.SS_Foundation
    _row_ = Wrap(stack.RK_RowStack, max_move=1, base_rank=cs.KING)
    _reserve_ = Wrap(stack.ReserveStack, max_cards=8)
    _hint_ = hint.CautiousDefaultHint

    def _create_game(self, **kw):
        """
        _create_game([rows=9, playcards=12, reserves=1, holdcards=8]) -> Layout

        :returns: current layout with piles and their texts set.

        :seealso: `pysollib.layouts.canfield` for keyword definitions.
        """
        kwdefault(kw, rows=9, playcards=12, reserves=1, holdcards=8,
                  reserve_texts=True, texts=True)
        return super(Soother, self)._create_game(**kw)

    def _start(self, rows=5, **kw):
        """
        Deal initial cards for new game.
          - s[rows] 5 ea up.

        .. \_start([rows=5]) -> None
        """
        for i in range(rows - 1):
            self.s['talon'].dealRow(frames=0)
        self.s['talon'].dealRow()

    _quickplay_ = quickplay.RowStack


PROVIDES = [StreetsAndAlleys, BeleagueredCastle, Citadel, Fortress,
            Chessboard, Stronghold, Fastness, Zerline, Bastion, TenByOne,
            Chequers, CastleOfIndolence, Zerline3Decks, Rittenhouse,
            Lightweight, CastleMount, SelectiveCastle, ExiledKings, Soother,
            CastlesEnd, PenelopesWeb]